// The NSpring Framework for .NET
// Copyright (c) 2003, Jeffrey Varszegi
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
// - Redistributions of source code must retain the above copyright notice, this 
// list of conditions and the following disclaimer.
// 
// - Redistributions in binary form must reproduce the above copyright notice, 
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// 
// - Neither the name of the NSpring project nor the names of its contributors may
// be used to endorse or promote products derived from this software without 
// specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

namespace NSpring.Logging.Loggers {
    
using System;
using System.IO;
using System.Text;

using NSpring.Logging.EventFormatters;

/// <summary>
/// An implementation of the Logger class that writes formatted Event information to a
/// System.IO.Stream .  The default formatter used is an instance of XMLEventFormatter
/// </summary>
public sealed class StreamLogger : Logger {
    private readonly static string NEW_LINE             = new string(new char[] {((char)13), ((char)10)});

    private Stream stream;
    private StreamWriter streamWriter;
    private bool isStreamClosingEnabled         = false;
    private bool isLineBreakingEnabled          = false;
    private bool headerWritten;

    private bool isPreambleEnabled              = false;

    /// <summary>
    /// Constructs a new instance using an XMLEventFormatter.  (A Stream will be required
    /// to be set before the logger's Open() method is called, or it will throw an exception)
    /// </summary>
    public StreamLogger() {
        eventFormatter = new XMLEventFormatter();
    }
    
    /// <summary>
    /// Constructs a new instance using an XMLEventFormatter
    /// </summary>
    /// <param name="_stream">The stream to which to write</param>
    public StreamLogger(Stream _stream) : this() {
        stream = _stream;
    }
    
    /// <summary>
    /// Constructs a new instance using an XMLEventFormatter
    /// </summary>
    /// <param name="_stream">The stream to which to write</param>
    /// <param name="streamClosingEnabled">The stream-closing strategy (see property IsStreamClosingEnabled)</param>
    public StreamLogger(Stream _stream, bool _isStreamClosingEnabled) : this(_stream) {
        isStreamClosingEnabled = _isStreamClosingEnabled;
    }
    
    /// <summary>
    /// Indicates whether the encoding's "preamble" (a set of bytes designed to 
    /// mark the beginning of a stream with encoding-type data) is written to the start 
    /// of a stream
    /// </summary>
    public bool IsPreambleEnabled {
        get {
            return isPreambleEnabled;
        }
        set {
            isPreambleEnabled = value;
        }
    }

    /// <summary>
    /// Gets and sets the underlying stream used by the logger.  Cannot be set while the
    /// logger is open
    /// </summary>
    public Stream Stream {
        get {
            return Stream;
        }
        set {
            lock (monitor) {
                if (IsOpen) {
                    throw new ApplicationException("Can''t change streams while the logger is open");
                }
            }
        }
    }
    
    /// <summary>
    /// Gets and sets whether this instance closes the underlying stream when it is closed.
    /// The default is false
    /// </summary>
    public bool IsStreamClosingEnabled {
        get {
            return isStreamClosingEnabled;
        }
        set {
            isStreamClosingEnabled = value;
        }
    }
    
    /// <summary>
    /// Gets and sets whether the instance inserts line breaks ((char)13 + (char)10) in the stream
    /// after every record.
    /// The default is false
    /// </summary>
    public bool IsLineBreakingEnabled {
        get {
            return isLineBreakingEnabled;
        }
        set {
            isLineBreakingEnabled = value;
        }
    }
	
    protected override void AcquireResources() {
        if (stream == null) {
            throw new ApplicationException("No Stream set-- cannot start");
        }
        streamWriter = new StreamWriter(stream, (isPreambleEnabled ? encoding : (new PreamblelessEncoding(encoding))), 8192);
        headerWritten = false;
    }
    
    protected override void ReleaseResources() {
        if (headerWritten) {
            string listFooter = eventFormatter.GetEventListFooter();
            if (listFooter != null) {
                streamWriter.Write(listFooter);
                if (isLineBreakingEnabled) {
                    streamWriter.Write(NEW_LINE);
                }
            }
        }
        try { streamWriter.Flush(); } catch {}
        if (isStreamClosingEnabled) {
            try { stream.Close(); } catch {}
            stream = null;
        }
        streamWriter = null;
    }
	
    protected override void Write(Event _event) {
        if (headerWritten == false) {
            string listHeader = eventFormatter.GetEventListHeader();
            if (listHeader != null) {
                streamWriter.Write(listHeader);
                if (isLineBreakingEnabled) {
                    streamWriter.Write(NEW_LINE);
                }
            }
            headerWritten = true;
        }
        
        streamWriter.Write(eventFormatter.Format(_event));
        if (isLineBreakingEnabled) {
            streamWriter.Write(NEW_LINE);
        }
        
        streamWriter.Flush();
    }
    
    protected override void Write(Event[] events, int length) {
        if (length <= 0) {
            return;
        }
        if (headerWritten == false) {
            string listHeader = eventFormatter.GetEventListHeader();
            if (listHeader != null) {
                streamWriter.Write(listHeader);
                if (isLineBreakingEnabled) {
                    streamWriter.Write(NEW_LINE);
                }
            }
            headerWritten = true;
        }
        
        if (isLineBreakingEnabled) {
            for(int x = 0; x < length; x++) {
                streamWriter.Write(eventFormatter.Format(events[x]));
                streamWriter.Write(NEW_LINE);
            }
        }
        else {
            for(int x = 0; x < length; x++) {
                streamWriter.Write(eventFormatter.Format(events[x]));
            }
        }
        
        streamWriter.Flush();
    }

    private class PreamblelessEncoding : Encoding {
        private byte[] EMPTY_BYTE_ARRAY                 = {};

        private Encoding innerEncoding;
        
        private PreamblelessEncoding() {}

        public PreamblelessEncoding(Encoding _encoding) {
            if (_encoding == null) {
                throw new ArgumentNullException();
            }
            innerEncoding = _encoding;
        }

        public Encoding InnerEncoding {
            get {
                return innerEncoding;
            }
        }

        public override string BodyName {
            get {
                return innerEncoding.BodyName;
            }
        }

        public override int CodePage {
            get {
                return innerEncoding.CodePage;
            }
        }

        public override string EncodingName {
            get {
                return innerEncoding.EncodingName;
            }
        }
        
        public override int GetByteCount(char[] chars) {
            return innerEncoding.GetByteCount(chars);
        }
        
        public override int GetByteCount(string s) {
            return innerEncoding.GetByteCount(s);
        }
        
        public override int GetByteCount(char[] chars, int index, int count) {
            return innerEncoding.GetByteCount(chars, index, count);
        }
        
        public override byte[] GetBytes(char[] chars) {
            return innerEncoding.GetBytes(chars);
        }
        
        public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) {
            return innerEncoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
        }
        
        public override byte[] GetBytes(char[] chars, int index, int count) {
            return innerEncoding.GetBytes(chars, index, count);
        }
        
        public override byte[] GetBytes(string s) {
            return innerEncoding.GetBytes(s);
        }
        
        public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) {
            return innerEncoding.GetBytes(s, charIndex, charCount, bytes, byteIndex);
        }
        
        public override int GetCharCount(byte[] bytes) {
            return innerEncoding.GetCharCount(bytes);
        }
        
        public override int GetCharCount(byte[] bytes, int index, int count) {
            return innerEncoding.GetCharCount(bytes, index, count);
        }
        
        public override char[] GetChars(byte[] bytes) {
            return innerEncoding.GetChars(bytes);
        }

        public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) {
            return innerEncoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
        }
        
        public override char[] GetChars(byte[] bytes, int index, int count) {
            return innerEncoding.GetChars(bytes, index, count);
        }
        
        public override Decoder GetDecoder() {
            return innerEncoding.GetDecoder();
        }
        
        public override Encoder GetEncoder() {
            return innerEncoding.GetEncoder();
        }
        
        public override int GetMaxByteCount(int charCount) {
            return innerEncoding.GetMaxByteCount(charCount);
        }
        
        public override int GetMaxCharCount(int byteCount) {
            return innerEncoding.GetMaxCharCount(byteCount);
        }

        public override byte[] GetPreamble() {
            return EMPTY_BYTE_ARRAY;
        }
        
        public override string GetString(byte[] bytes) {
            return innerEncoding.GetString(bytes);
        }
        
        public override string GetString(byte[] bytes, int index, int count) {
            return innerEncoding.GetString(bytes, index, count);
        }
        
        public override string HeaderName {
            get {
                return innerEncoding.HeaderName;
            }
        }
        
        public override bool IsBrowserDisplay {
            get {
                return innerEncoding.IsBrowserDisplay;
            }
        }
        
        public override bool IsBrowserSave {
            get {
                return innerEncoding.IsBrowserSave;
            }
        }
        
        public override bool IsMailNewsDisplay {
            get {
                return innerEncoding.IsMailNewsDisplay;
            }
        }
        
        public override bool IsMailNewsSave {
            get {
                return innerEncoding.IsMailNewsSave;
            }
        }
        
        public override string WebName {
            get {
                return innerEncoding.WebName;
            }
        }
        
        public override int WindowsCodePage {
            get {
                return innerEncoding.WindowsCodePage;
            }
        }

    }
    
}

}
